
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       param.c
  * @author     baiyang
  * @date       2021-7-12
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include <stdio.h>
#include <float.h>
#include <stdbool.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

#include <rtconfig.h>

#if defined(RT_USING_FAL)
#include <fal.h>
#endif

#include "param.h"
#include <file_manager/file_manager.h>
#include <common/yaml/yaml.h>
#include <common/gp_math/gp_mathlib.h>
#include <board_config/borad_config.h>
#include <notify/notify.h>
#include <common/console/console.h>
#include <gcs_mavlink/gcs.h>
/*-----------------------------------macro------------------------------------*/
#define PARAM_FILE_NAME                "/sys/param.yaml"
#define PARAM_DIR                      "/sys"

#if !defined(PARAM_FAL_PARTITION_NAME)
#define PARAM_FAL_PARTITION_NAME "param"
#endif

#define GCS_SEND_PARAM(name, type, v) gcs_send_parameter_value(name, type, v)
/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/
static int _yaml_read_handler(void *data, unsigned char *buffer, size_t size, size_t *size_read);
/*----------------------------------variable----------------------------------*/

#if defined(HAL_USE_EEPROM_PARAM)
#if defined(RT_USING_FAL)
#pragma pack(1)
struct param_group_info {
    char name[PARAM_MAX_NAME_SIZE + 1];
    uint8_t element_num;
};

struct param_element_info {
    char name[PARAM_MAX_NAME_SIZE + 1];
    uint8_t type;
    param_value_t val;
};

struct EEPROM_header {
    uint8_t magic[2];
    uint8_t revision;
    uint8_t group_num;
};
#pragma pack()

_Static_assert(sizeof(struct EEPROM_header) == 4, "Bad EEPROM_header size!");

static const struct fal_partition *param_part_dev = NULL;

// values filled into the EEPROM header
static const uint8_t        k_EEPROM_magic0      = 0x4D;
static const uint8_t        k_EEPROM_magic1      = 0x42; ///< "MB"
static const uint8_t        k_EEPROM_revision    = 1; ///< current format revision

static uint32_t param_total_size = 0;

#else
    #error "Need to defined RT_USING_FAL."
#endif
#endif

static uint32_t params_count;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
static void _parse_yaml(yaml_token_t *token, PARAM_YAML_PARSE_STATE* status)
{
    static char group_name[30];
    static char param_name[30];
    static char content[20];
    static uint8_t parse_yaml_step = 0;
    
    switch(*status)
    {
        case PARAM_YAML_PARSE_START:
        {
            switch(parse_yaml_step)
            {
                case 0:
                {
                    if(token->type == YAML_BLOCK_MAPPING_START_TOKEN) {
                        parse_yaml_step ++;
                    }
                }break;

                case 1:
                {
                    if(token->type == YAML_KEY_TOKEN) {
                        parse_yaml_step ++;
                    }
                }break;

                case 2:
                {
                    if(token->type == YAML_SCALAR_TOKEN \
                       && strncmp("kind", (const char *)token->data.scalar.value, token->data.scalar.length)==0) {
                        parse_yaml_step ++;
                    } else {
                        parse_yaml_step = 0;
                    }
                }break;

                case 3:
                {
                    if(token->type == YAML_VALUE_TOKEN) {
                        parse_yaml_step ++;
                    }
                }break;

                case 4:
                {
                    if(token->type == YAML_SCALAR_TOKEN \
                       && (strncmp("parameter", (const char *)token->data.scalar.value, token->data.scalar.length)==0)) {
                        *status = PARAM_YAML_PARSE_GROUP_NAME;
                    }

                    parse_yaml_step = 0;
                }break;
            }
        }break;

        case PARAM_YAML_PARSE_GROUP_NAME:
        {
            switch(parse_yaml_step)
            {
                case 0:
                {
                    if (token->type == YAML_KEY_TOKEN) {
                        parse_yaml_step ++;
                    } else if (token->type == YAML_STREAM_END_TOKEN) {
                        *status = PARAM_YAML_PARSE_START;
                    }
                }break;

                case 1:
                {
                    if (token->type == YAML_SCALAR_TOKEN) {
                        rt_memcpy(group_name, token->data.scalar.value, MIN(token->data.scalar.length, sizeof(group_name)));
                        group_name[MIN(token->data.scalar.length, sizeof(group_name))] = '\0';
                        parse_yaml_step ++;
                    } else {
                        parse_yaml_step = 0;
                    }
                }break;

                case 2:
                {
                    if(token->type == YAML_VALUE_TOKEN) {
                        parse_yaml_step ++;
                    }
                }break;

                case 3:
                {
                    if(token->type == YAML_BLOCK_MAPPING_START_TOKEN) {
                        *status = PARAM_YAML_PARSE_PARAM;
                    }

                    parse_yaml_step = 0;
                }break;
            }
        }break;

        case PARAM_YAML_PARSE_PARAM:
        {
            switch(parse_yaml_step)
            {
                case 0:
                {
                    if (token->type == YAML_KEY_TOKEN) {
                        parse_yaml_step ++;
                    } else if (token->type == YAML_BLOCK_END_TOKEN) {
                        *status = PARAM_YAML_PARSE_GROUP_NAME;
                    }
                }break;

                case 1:
                {
                    if (token->type == YAML_SCALAR_TOKEN) {
                        rt_memcpy(param_name, token->data.scalar.value, MIN(token->data.scalar.length, sizeof(param_name)));
                        param_name[MIN(token->data.scalar.length, sizeof(param_name))] = '\0';
                        parse_yaml_step ++;
                    } else {
                        parse_yaml_step = 0;
                    }
                }break;

                case 2:
                {
                    if (token->type == YAML_VALUE_TOKEN) {
                        parse_yaml_step ++;
                    }
                }break;

                case 3:
                {
                    if (token->type == YAML_SCALAR_TOKEN) {
                        rt_memcpy(content, token->data.scalar.value, MIN(token->data.scalar.length, sizeof(content)));
                        content[MIN(token->data.scalar.length, sizeof(content))] = '\0';

                        param_set_val_by_full_name(group_name, param_name, content);

                        *status = PARAM_YAML_PARSE_PARAM;
                    }

                    parse_yaml_step = 0;
                }break;
            }
        }break;

    }
}

#if defined(HAL_USE_EEPROM_PARAM)
static void _convert_element(param_t* p, struct param_element_info *element)
{
    switch (p->type) {
    case PARAM_TYPE_INT8:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.i8 = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.i8 = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.i8 = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.i8 = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.i8 = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.i8 = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.i8 = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.i8 = element->val.lf;
        }
        break;

    case PARAM_TYPE_UINT8:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.u8 = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.u8 = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.u8 = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.u8 = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.u8 = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.u8 = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.u8 = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.u8 = element->val.lf;
        }
        break;

    case PARAM_TYPE_INT16:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.i16 = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.i16 = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.i16 = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.i16 = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.i16 = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.i16 = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.i16 = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.i16 = element->val.lf;
        }
        break;

    case PARAM_TYPE_UINT16:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.u16 = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.u16 = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.u16 = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.u16 = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.u16 = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.u16 = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.u16 = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.u16 = element->val.lf;
        }
        break;

    case PARAM_TYPE_INT32:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.i32 = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.i32 = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.i32 = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.i32 = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.i32 = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.i32 = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.i32 = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.i32 = element->val.lf;
        }
        break;

    case PARAM_TYPE_UINT32:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.u32 = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.u32 = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.u32 = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.u32 = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.u32 = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.u32 = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.u32 = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.u32 = element->val.lf;
        }
        break;

    case PARAM_TYPE_FLOAT:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.f = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.f = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.f = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.f = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.f = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.f = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.f = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.f = element->val.lf;
        }
        break;

    case PARAM_TYPE_DOUBLE:
        if (p->type == PARAM_TYPE_INT8) {
            p->val.lf = element->val.i8;
        } else if (p->type == PARAM_TYPE_UINT8) {
            p->val.lf = element->val.u8;
        } else if (p->type == PARAM_TYPE_INT16) {
            p->val.lf = element->val.i16;
        } else if (p->type == PARAM_TYPE_UINT16) {
            p->val.lf = element->val.u16;
        } else if (p->type == PARAM_TYPE_INT32) {
            p->val.lf = element->val.i32;
        } else if (p->type == PARAM_TYPE_UINT32) {
            p->val.lf = element->val.u32;
        } else if (p->type == PARAM_TYPE_FLOAT) {
            p->val.lf = element->val.f;
        } else if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.lf = element->val.lf;
        }
        break;

    default:
        break;
    }

}

static void _parse_eeprom(struct param_group_info *group_info, struct param_element_info *element)
{
    param_t* p = param_get_by_full_name(group_info->name, element->name);
    if(p != NULL){
        if (p->type == element->type) {
            p->val = element->val;
        } else {
            _convert_element(p, element);
        }
    }else{
        rt_kprintf("can not find %s in group %s\n", group_info->name, element->name);
        return;
    }

    switch (p->type) {
    case PARAM_TYPE_INT8:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.i8, sizeof(int8_t));
        }
        break;

    case PARAM_TYPE_UINT8:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.u8, sizeof(uint8_t));
        }
        break;

    case PARAM_TYPE_INT16:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.i16, sizeof(int16_t));
        }
        break;

    case PARAM_TYPE_UINT16:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.u16, sizeof(uint16_t));
        }
        break;

    case PARAM_TYPE_INT32:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.i32, sizeof(int32_t));
        }
        break;

    case PARAM_TYPE_UINT32:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.u32, sizeof(uint32_t));
        }
        break;

    case PARAM_TYPE_FLOAT:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.f, sizeof(float));
        }
        break;

    case PARAM_TYPE_DOUBLE:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.lf, sizeof(double));
        }
        break;

    default:
        break;
    }
}
#endif

static void param_scan_list()
{
    param_t* p;
    param_group_t* gp;

    if (params_count != 0) {
        params_count = 0;
    }

    for (int i = 0; i < PARAM_GROUP_COUNT; i++) {
        gp = param_group_list[i];
        p = gp->content;

        for (int j = 0; j < gp->param_num; j++) {
            p->idx = params_count;

            params_count++;
            p++;
        }
    }

    if (params_count > PARAM_INDEX_NONE) {
        console_panic("[param]: Parameters exceeds the maximum number\n");
    }
}

param_t* param_get_first()
{
    param_group_t* gp = param_group_list[0];
    param_t* p = &gp->content[0];

    if (gp != NULL && p!= NULL) {
        return p;
    }

    return NULL;
}

param_t* param_get_next_scalar(const param_t* param)
{
    uint32_t idx = 0;
    const uint16_t next_idx = param->idx + 1;

    param_t* p;
    param_group_t* gp;

    if (next_idx >= params_count) {
        return NULL;
    }

    for(int j = 0 ; j < PARAM_GROUP_COUNT; j++) {
        gp = param_group_list[j];
        p = gp->content;

        if (p != NULL && (idx + gp->param_num <= next_idx)) {
            idx += gp->param_num;
            continue;
        }

        for(int i= 0 ; i < gp->param_num ; i++) {
            if (idx == next_idx) {
                return p;
            }
            p++;
            idx++;
        }
    }

    return NULL;
}

param_t* param_get_by_name(const char* param_name)
{
    param_t* p;
    param_group_t* gp;
    for(int j = 0 ; j < PARAM_GROUP_COUNT; j++) {
        gp = param_group_list[j];
        p = gp->content;
        for(int i= 0 ; i < gp->param_num ; i++) {
            if(strcmp(param_name, p->name) == 0)
                return p;
            p++;
        }
    }

    return NULL;
}

param_t* param_get_by_index(const uint32_t index)
{
    uint32_t idx = 0;

    param_t* p;
    param_group_t* gp;

    for(int j = 0 ; j < PARAM_GROUP_COUNT; j++) {
        gp = param_group_list[j];
        p = gp->content;

        if (p != NULL && (idx + gp->param_num <= index)) {
            idx += gp->param_num;
            continue;
        }

        for(int i= 0 ; i < gp->param_num ; i++) {
            if (idx == index) {
                return p;
            }
            p++;
            idx++;
        }
    }

    return NULL;
}

param_t* param_get_by_full_name(const char* group_name, const char* param_name)
{
    param_t* p;
    param_group_t* gp;
    for (int j = 0; j < PARAM_GROUP_COUNT; j++){
        gp = param_group_list[j];
        if (strcmp(group_name, gp->name)==0){
            p = gp->content;
            for (int i= 0 ; i < gp->param_num; i++){
                if (strcmp(param_name, p->name)==0) {
                    return p;
                }
                
                p++;
            }
        }
    }
    
    return NULL;
}

param_t* param_get_by_variable(void* variable)
{
    param_t* p;
    param_group_t* gp;

    for (int i = 0; i < PARAM_GROUP_COUNT; i++) {
        gp = param_group_list[i];
        p = gp->content;

        for (int j = 0; j < gp->param_num; j++) {
            if (p->user_param != NULL && p->user_param == variable) {
                return p;
            }
            p++;
        }
    }

    return NULL;
}

param_t* param_get_by_variable2(const char* group_name, void* variable)
{
    param_t* p;
    param_group_t* gp;

    uint32_t len = MIN(strlen(group_name),PARAM_MAX_NAME_SIZE);

    for (int i = 0; i < PARAM_GROUP_COUNT; i++){
        gp = param_group_list[i];
        if (strncmp(group_name, gp->name, len)==0){
            p = gp->content;
            for (int j= 0; j < gp->param_num; j++){
                if (p->user_param != NULL && p->user_param == variable) {
                    return p;
                }
                p++;
            }
        }
    }

    return NULL;
}

void* param_find_variable(const char* param_name, enum param_type_t *ptype)
{
    param_t* p;
    param_group_t* gp;
    for(int j = 0 ; j < PARAM_GROUP_COUNT; j++) {
        gp = param_group_list[j];
        p = gp->content;
        for(int i= 0 ; i < gp->param_num ; i++) {
            if(strcmp(param_name, p->name) == 0) {
                *ptype = (enum param_type_t)p->type;
                return p->user_param;
            }
            p++;
        }
    }

    return NULL;
}

void* param_find_variable2(const char* group_name, const char* param_name, enum param_type_t *ptype)
{
    param_t* p;
    param_group_t* gp;
    for (int j = 0; j < PARAM_GROUP_COUNT; j++){
        gp = param_group_list[j];
        if (strcmp(group_name, gp->name)==0){
            p = gp->content;
            for (int i= 0 ; i < gp->param_num; i++){
                if (strcmp(param_name, p->name)==0) {
                    *ptype = (enum param_type_t)p->type;
                    return p->user_param;
                }
                
                p++;
            }
        }
    }

    return NULL;
}

uint32_t param_get_count(void)
{
    return params_count;
}

float param_cast_to_float(param_t* param)
{
    float param_value;

    switch (param->type) {
    case PARAM_TYPE_DOUBLE: {
        double lf;
        if (param->user_param) {
            rt_memcpy(&lf, param->user_param, sizeof(double));
            param_value = (float)lf;
        } else {
            param_value = (float)param->val.lf;
        }
    }
        break;

    case PARAM_TYPE_FLOAT:
        if (param->user_param) {
            rt_memcpy(&param_value, param->user_param, sizeof(float));
        } else {
            param_value = param->val.f;
        }
        break;

    case PARAM_TYPE_INT32: {
        int32_t i32;
        if (param->user_param) {
            rt_memcpy(&i32, param->user_param, sizeof(int32_t));
            param_value = (float)i32;
        } else {
            param_value = param->val.i32;
        }
    }
        break;

    case PARAM_TYPE_UINT32: {
        uint32_t u32;
        if (param->user_param) {
            rt_memcpy(&u32, param->user_param, sizeof(uint32_t));
            param_value = (float)u32;
        } else {
            param_value = param->val.u32;
        }
    }
        break;

    case PARAM_TYPE_INT16:
        if (param->user_param) {
            param_value = *((int16_t *)param->user_param);
        } else {
            param_value = param->val.i16;
        }
        break;

    case PARAM_TYPE_UINT16:
        if (param->user_param) {
            param_value = *((uint16_t *)param->user_param);
        } else {
            param_value = param->val.u16;
        }
        break;

    case PARAM_TYPE_INT8:
        if (param->user_param) {
            param_value = *((int8_t *)param->user_param);
        } else {
            param_value = param->val.i8;
        }
        break;

    case PARAM_TYPE_UINT8:
        if (param->user_param) {
            param_value = *((uint8_t *)param->user_param);
        } else {
            param_value = param->val.u8;
        }
        break;

    default:
        param_value = 0.0f;
        break;
    }

    return param_value;
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   val  
  * @param[out]  
  * @retval      
  * @note        用于接收地面站设置参数的命令，并保存
  */
int param_set_val(param_t* param, void* val)
{
    float _value = *((float *)val);
    float v = 0;

    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;
    
    switch (param->type) {
    case PARAM_TYPE_INT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -128, 127);
        param->val.i8 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i8, sizeof(int8_t));
        }
        break;

    case PARAM_TYPE_UINT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 255);
        param->val.u8 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u8, sizeof(uint8_t));
        }
        break;

    case PARAM_TYPE_INT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -32768, 32767);
        param->val.i16 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i16, sizeof(int16_t));
        }
        break;

    case PARAM_TYPE_UINT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 65535);
        param->val.u16 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u16, sizeof(uint16_t));
        }
        break;

    case PARAM_TYPE_INT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, INT32_MIN, INT32_MAX);
        param->val.i32 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i32, sizeof(int32_t));
        }
        break;

    case PARAM_TYPE_UINT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, UINT32_MAX);
        param->val.u32 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u32, sizeof(uint32_t));
        }
        break;

    case PARAM_TYPE_FLOAT:
        param->val.f = _value;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.f, sizeof(float));
        }
        break;

    case PARAM_TYPE_DOUBLE:
        param->val.lf = _value;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.lf, sizeof(double));
        }
        break;

    default:
        return -1;
    }
    
    param_save();

    return 0;
}

int param_set_val_by_full_name(char* group_name, char* param_name, char* val)
{
    param_t* p = param_get_by_full_name(group_name, param_name);
    if(p != NULL){
        if (p->type == PARAM_TYPE_INT8) {
            p->val.i8 = atoi(val);
        }

        if (p->type == PARAM_TYPE_UINT8) {
            p->val.u8 = atoi(val);
        }

        if (p->type == PARAM_TYPE_INT16) {
            p->val.i16 = atoi(val);
        }

        if (p->type == PARAM_TYPE_UINT16) {
            p->val.u16 = atoi(val);
        }

        if (p->type == PARAM_TYPE_INT32) {
            p->val.i32 = atoi(val);
        }

        if (p->type == PARAM_TYPE_UINT32) {
            p->val.u32 = atoi(val);
        }

        if (p->type == PARAM_TYPE_FLOAT) {
            p->val.f = atof(val);
        }

        if (p->type == PARAM_TYPE_DOUBLE) {
            p->val.lf = atof(val);
        }
    }else{
        rt_kprintf("can not find %s in group %s\n", param_name, group_name);
        return 1;
    }

    switch (p->type) {
    case PARAM_TYPE_INT8:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.i8, sizeof(int8_t));
        }
        break;

    case PARAM_TYPE_UINT8:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.u8, sizeof(uint8_t));
        }
        break;

    case PARAM_TYPE_INT16:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.i16, sizeof(int16_t));
        }
        break;

    case PARAM_TYPE_UINT16:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.u16, sizeof(uint16_t));
        }
        break;

    case PARAM_TYPE_INT32:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.i32, sizeof(int32_t));
        }
        break;

    case PARAM_TYPE_UINT32:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.u32, sizeof(uint32_t));
        }
        break;

    case PARAM_TYPE_FLOAT:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.f, sizeof(float));
        }
        break;

    case PARAM_TYPE_DOUBLE:
        if (p->user_param) {
            rt_memcpy(p->user_param, &p->val.lf, sizeof(double));
        }
        break;

    default:
        break;
    }

    return 0;
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   val  
  * @param[out]  
  * @retval      
  * @note        用于接收地面站设置参数的命令，并保存
  */
int param_set_float(param_t* param, float _value)
{
    float v = 0;

    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;
    
    switch (param->type) {
    case PARAM_TYPE_INT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -128, 127);
        param->val.i8 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i8, sizeof(int8_t));
        }
        break;

    case PARAM_TYPE_UINT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 255);
        param->val.u8 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u8, sizeof(uint8_t));
        }
        break;

    case PARAM_TYPE_INT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -32768, 32767);
        param->val.i16 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i16, sizeof(int16_t));
        }
        break;

    case PARAM_TYPE_UINT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 65535);
        param->val.u16 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u16, sizeof(uint16_t));
        }
        break;

    case PARAM_TYPE_INT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, INT32_MIN, INT32_MAX);
        param->val.i32 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i32, sizeof(int32_t));
        }
        break;

    case PARAM_TYPE_UINT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, UINT32_MAX);
        param->val.u32 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u32, sizeof(uint32_t));
        }
        break;

    case PARAM_TYPE_FLOAT:
        param->val.f = _value;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.f, sizeof(float));
        }
        break;

    case PARAM_TYPE_DOUBLE:
        param->val.lf = _value;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.lf, sizeof(double));
        }
        break;

    default:
        return -1;
    }

    return 0;
}

/**
  * @brief       
  * @param[in]   param  
  * @param[out]  
  * @retval      
  * @note        用于通过地面站保存参数，param->user_param为空时不报错
  */
void param_save_float(param_t* param)
{
    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;

    if (!param->user_param) {
        return;
    }

    switch (param->type) {
    case PARAM_TYPE_INT8:
        rt_memcpy(&param->val.i8, param->user_param, sizeof(int8_t));
        break;

    case PARAM_TYPE_UINT8:
        rt_memcpy(&param->val.u8, param->user_param, sizeof(uint8_t));
        break;

    case PARAM_TYPE_INT16:
        rt_memcpy(&param->val.i16, param->user_param, sizeof(int16_t));
        break;

    case PARAM_TYPE_UINT16:
        rt_memcpy(&param->val.u16, param->user_param, sizeof(uint16_t));
        break;

    case PARAM_TYPE_INT32:
        rt_memcpy(&param->val.i32, param->user_param, sizeof(int32_t));
        break;

    case PARAM_TYPE_UINT32:
        rt_memcpy(&param->val.u32, param->user_param, sizeof(uint32_t));
        break;

    case PARAM_TYPE_FLOAT:
        rt_memcpy(&param->val.f, param->user_param, sizeof(float));
        break;

    case PARAM_TYPE_DOUBLE:
        rt_memcpy(&param->val.lf, param->user_param, sizeof(double));
        break;

    default:
        return;
    }

    param_save();

    GCS_SEND_PARAM(param->name, param->type, param_cast_to_float(param));
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   variable  
  * @param[out]  
  * @retval      
  * @note        将功能模块中的参数和参数模块中的对应参数链接起来，
  *              并获取参数模块中对应参数的值。
  */
void param_link_variable(param_t* param, void* variable)
{
    if (!param) {
        brd_config_error("[param]: <link variable> param is NULL");
    }

    param->user_param = variable;

    switch (param->type) {
    case PARAM_TYPE_INT8:
        rt_memcpy(variable, &param->val.i8, sizeof(int8_t));
        break;

    case PARAM_TYPE_UINT8:
        rt_memcpy(variable, &param->val.u8, sizeof(uint8_t));
        break;

    case PARAM_TYPE_INT16:
        rt_memcpy(variable, &param->val.i16, sizeof(int16_t));
        break;

    case PARAM_TYPE_UINT16:
        rt_memcpy(variable, &param->val.u16, sizeof(uint16_t));
        break;

    case PARAM_TYPE_INT32:
        rt_memcpy(variable, &param->val.i32, sizeof(int32_t));
        break;

    case PARAM_TYPE_UINT32:
        rt_memcpy(variable, &param->val.u32, sizeof(uint32_t));
        break;

    case PARAM_TYPE_FLOAT:
        rt_memcpy(variable, &param->val.f, sizeof(float));
        break;

    case PARAM_TYPE_DOUBLE:
        rt_memcpy(variable, &param->val.lf, sizeof(double));
        break;

    default:
        return;
    }
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   _value  
  * @param[out]  
  * @retval      
  * @note        设置被连接的参数的值
  */
void param_set_obj(param_t* param, double _value)
{
    double v = 0;

    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;

    if (!param->user_param) {
#if CONFIG_HAL_BOARD != HAL_BOARD_RTTHREAD
        brd_config_error("[param]: <%s> no link module parameters", param->name);
#else
        gcs_send_text(MAV_SEVERITY_ERROR, "[param]: <%s> no link module parameters", param->name);
        console_printf("[param]: <%s> no link module parameters", param->name);
        return;
#endif
    }

    switch (param->type) {
    case PARAM_TYPE_INT8: {
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -128, 127);

        int8_t val = (int8_t)v;
        rt_memcpy(param->user_param, &val, sizeof(int8_t));
        } break;

    case PARAM_TYPE_UINT8: {
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 255);

        uint8_t val = (uint8_t)v;
        rt_memcpy(param->user_param, &val, sizeof(uint8_t));
        } break;

    case PARAM_TYPE_INT16: {
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -32768, 32767);

        int16_t val = (int16_t)v;
        rt_memcpy(param->user_param, &val, sizeof(int16_t));
        } break;

    case PARAM_TYPE_UINT16: {
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 65535);

        uint16_t val = (uint16_t)v;
        rt_memcpy(param->user_param, &val, sizeof(uint16_t));
        } break;

    case PARAM_TYPE_INT32: {
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_double(v, INT32_MIN, INT32_MAX);

        int32_t val = (int32_t)v;
        rt_memcpy(param->user_param, &val, sizeof(int32_t));
        } break;

    case PARAM_TYPE_UINT32: {
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_double(v, 0, UINT32_MAX);

        uint32_t val = (uint32_t)v;
        rt_memcpy(param->user_param, &val, sizeof(uint32_t));
        } break;

    case PARAM_TYPE_FLOAT: {
        float val = _value;
        rt_memcpy(param->user_param, &val, sizeof(float));
        } break;

    case PARAM_TYPE_DOUBLE: {
        double val = _value;
        rt_memcpy(param->user_param, &val, sizeof(double));
        } break;

    default:
        return;
    }
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   _value  
  * @param[out]  
  * @retval      
  * @note        设置被连接的参数的值,并将此参数发送到地面站
  */
void param_set_obj_and_notify(param_t* param, double _value)
{
    param_set_obj(param, _value);
    GCS_SEND_PARAM(param->name, param->type, (float)_value);
}

/**
  * @brief       
  * @param[in]   param  
  * @param[out]  
  * @retval      
  * @note        
  */
void param_save_obj(param_t* param)
{
    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;

    if (!param->user_param) {
#if CONFIG_HAL_BOARD != HAL_BOARD_RTTHREAD
        brd_config_error("[param]: <%s> no link module parameters", param->name);
#else
        gcs_send_text(MAV_SEVERITY_ERROR, "[param]: <%s> no link module parameters", param->name);
        console_printf("[param]: <%s> no link module parameters", param->name);
        return;
#endif
    }

    switch (param->type) {
    case PARAM_TYPE_INT8:
        rt_memcpy(&param->val.i8, param->user_param, sizeof(int8_t));
        break;

    case PARAM_TYPE_UINT8:
        rt_memcpy(&param->val.u8, param->user_param, sizeof(uint8_t));
        break;

    case PARAM_TYPE_INT16:
        rt_memcpy(&param->val.i16, param->user_param, sizeof(int16_t));
        break;

    case PARAM_TYPE_UINT16:
        rt_memcpy(&param->val.u16, param->user_param, sizeof(uint16_t));
        break;

    case PARAM_TYPE_INT32:
        rt_memcpy(&param->val.i32, param->user_param, sizeof(int32_t));
        break;

    case PARAM_TYPE_UINT32:
        rt_memcpy(&param->val.u32, param->user_param, sizeof(uint32_t));
        break;

    case PARAM_TYPE_FLOAT:
        rt_memcpy(&param->val.f, param->user_param, sizeof(float));
        break;

    case PARAM_TYPE_DOUBLE:
        rt_memcpy(&param->val.lf, param->user_param, sizeof(double));
        break;

    default:
        return;
    }

    param_save();
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   _value  
  * @param[out]  
  * @retval      
  * @note        设置被连接的参数和参数模块中对应变量的值，并保存到sd卡中
  */
void param_set_and_save(param_t* param, double _value)
{
    double v = 0;

    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;

    if (!param->user_param) {
#if CONFIG_HAL_BOARD != HAL_BOARD_RTTHREAD
        brd_config_error("[param]: <%s> no link module parameters", param->name);
#else
        gcs_send_text(MAV_SEVERITY_ERROR, "[param]: <%s> no link module parameters", param->name);
        console_printf("[param]: <%s> no link module parameters", param->name);
        return;
#endif
    }

    switch (param->type) {
    case PARAM_TYPE_INT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -128, 127);
        param->val.i8 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i8, sizeof(int8_t));
        }
        break;

    case PARAM_TYPE_UINT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 255);
        param->val.u8 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u8, sizeof(uint8_t));
        }
        break;

    case PARAM_TYPE_INT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -32768, 32767);
        param->val.i16 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i16, sizeof(int16_t));
        }
        break;

    case PARAM_TYPE_UINT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 65535);
        param->val.u16 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u16, sizeof(uint16_t));
        }
        break;

    case PARAM_TYPE_INT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_double(v, INT32_MIN, INT32_MAX);
        param->val.i32 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.i32, sizeof(int32_t));
        }
        break;

    case PARAM_TYPE_UINT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_double(v, 0, UINT32_MAX);
        param->val.u32 = v;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.u32, sizeof(uint32_t));
        }
        break;

    case PARAM_TYPE_FLOAT:
        param->val.f = (float)_value;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.f, sizeof(float));
        }
        break;

    case PARAM_TYPE_DOUBLE:
        param->val.lf = _value;

        if (param->user_param) {
            rt_memcpy(param->user_param, &param->val.lf, sizeof(double));
        }
        break;

    default:
        return;
    }

    param_save();
}

/**
  * @brief       
  * @param[in]   param  
  * @param[in]   _value  
  * @param[out]  
  * @retval      
  * @note        如果值被改变，设置被连接的参数和参数模块中对应变量的值，并保存到sd卡中
  */
void param_set_and_save_ifchanged(param_t* param, double _value)
{
    double v = 0;
    bool should_save = false;

    // add a small amount before casting parameter values
    // from float to integer to avoid truncating to the
    // next lower integer value.
    float rounding_addition = 0.01f;

    if (!param->user_param) {
#if CONFIG_HAL_BOARD != HAL_BOARD_RTTHREAD
        brd_config_error("[param]: <%s> no link module parameters", param->name);
#else
        gcs_send_text(MAV_SEVERITY_ERROR, "[param]: <%s> no link module parameters", param->name);
        console_printf("[param]: <%s> no link module parameters", param->name);
        return;
#endif
    }

    switch (param->type) {
    case PARAM_TYPE_INT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -128, 127);

        if (param->val.i8 != (int8_t)v) {
            int8_t val = (int8_t)v;
            rt_memcpy(param->user_param, &val, sizeof(int8_t));

            param->val.i8 = v;
            should_save = true;
        }
        break;

    case PARAM_TYPE_UINT8:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 255);

        if (param->val.u8 != (uint8_t)v) {
            uint8_t val = (uint8_t)v;
            rt_memcpy(param->user_param, &val, sizeof(uint8_t));
            
            param->val.u8 = v;
            should_save = true;
        }
        break;

    case PARAM_TYPE_INT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, -32768, 32767);

        if (param->val.i16 != (int16_t)v) {
            int16_t val = (int16_t)v;
            rt_memcpy(param->user_param, &val, sizeof(int16_t));
            
            param->val.i16 = v;
            should_save = true;
        }
        break;

    case PARAM_TYPE_UINT16:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_float(v, 0, 65535);

        if (param->val.u16 != (uint16_t)v) {
            uint16_t val = (uint16_t)v;
            rt_memcpy(param->user_param, &val, sizeof(uint16_t));
            
            param->val.u16 = v;
            should_save = true;
        }
        break;

    case PARAM_TYPE_INT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_double(v, INT32_MIN, INT32_MAX);

        if (param->val.i32 != (int32_t)v) {
            int32_t val = (int32_t)v;
            rt_memcpy(param->user_param, &val, sizeof(int32_t));
            
            param->val.i32 = v;
            should_save = true;
        }
        break;

    case PARAM_TYPE_UINT32:
        if (_value < 0) rounding_addition = -rounding_addition;
        v = _value+rounding_addition;
        v = math_constrain_double(v, 0, UINT32_MAX);

        if (param->val.u32 != (uint32_t)v) {
            uint32_t val = (uint32_t)v;
            rt_memcpy(param->user_param, &val, sizeof(uint32_t));
            
            param->val.u32 = v;
            should_save = true;
        }
        break;

    case PARAM_TYPE_FLOAT: {
        float val = (float)_value;
        if (!math_flt_equal(param->val.f,val)) {
            rt_memcpy(param->user_param, &val, sizeof(float));
            
            param->val.f = val;
            should_save = true;
        }
        } break;

    case PARAM_TYPE_DOUBLE:
        if (!math_flt_equal(param->val.lf,_value)) {
            rt_memcpy(param->user_param, &_value, sizeof(double));
            param->val.lf = _value;
            should_save = true;
        }
        break;

    default:
        return;
    }

    param_save();
}

#if defined(HAL_USE_EEPROM_PARAM)
void param_save_to_file(void)
{
    uint32_t offset = 0;

    static struct EEPROM_header hdr;
    static struct param_group_info group_info;
    static struct param_element_info element_info;
    static const uint8_t delimiter[2] = {0x55,0xAA};
    static const uint8_t terminator[2] = {0x1F,0x04};

    uint8_t group_num = PARAM_GROUP_COUNT;

    int result = 0;
    param_t* p = NULL;
    param_group_t* gp;

    rt_memset(&group_info, 0, sizeof(struct param_group_info));

    // write the header
    hdr.magic[0]  = k_EEPROM_magic0;
    hdr.magic[1]  = k_EEPROM_magic1;
    hdr.revision  = k_EEPROM_revision;
    hdr.group_num = group_num;

    if(param_part_dev != NULL) {

        if (param_total_size == 0) {
            fal_partition_erase_all(param_part_dev);
        } else {
            fal_partition_erase(param_part_dev, 0, param_total_size);
        }

        fal_partition_write(param_part_dev, offset, (const uint8_t *)&hdr, sizeof(struct EEPROM_header));

        offset += sizeof(struct EEPROM_header);

        for(int j = 0 ; j < group_num ; j++) {
            gp = param_group_list[j];
            group_info.element_num = gp->param_num;
            rt_strcpy(group_info.name, gp->name);
            fal_partition_write(param_part_dev, offset, (const uint8_t *)&group_info, sizeof(struct param_group_info));
            offset += sizeof(struct param_group_info);

            p = gp->content;
            for(int i= 0 ; i < gp->param_num ; i++){
                rt_strcpy(element_info.name, p->name);
                element_info.type = p->type;
                element_info.val = p->val;

                result = fal_partition_write(param_part_dev, offset, (const uint8_t *)&element_info, sizeof(struct param_element_info));

                offset += sizeof(struct param_element_info);
                p++;
            }

            if (j == group_num -1) {
                fal_partition_write(param_part_dev, offset, terminator, sizeof(terminator));
                offset += sizeof(terminator);
            } else {
                fal_partition_write(param_part_dev, offset, delimiter, sizeof(delimiter));
                offset += sizeof(delimiter);
            }
        }

    }
}

void param_load(void)
{
    uint32_t offset = 0;
    int8_t result = 0;

    static struct EEPROM_header hdr;
    struct param_group_info group_info;
    struct param_element_info element_info;

    uint8_t spacer[2];

    if (param_part_dev == NULL) {
        rt_kprintf("Device %s NOT found. param load failed.\n", PARAM_FAL_PARTITION_NAME);
        return;
    }

    if (fal_partition_read(param_part_dev, offset, (uint8_t *)&hdr, sizeof(struct EEPROM_header)) < 0) {
        rt_kprintf("[param] read fail.\n");
        return;
    }

    offset += sizeof(struct EEPROM_header);

    if (hdr.magic[0] != k_EEPROM_magic0 || hdr.magic[1] != k_EEPROM_magic1) {
        param_save_to_file();
        rt_kprintf("Initialize the parameter storage structure.\n");
        return;
    }

    for(uint8_t j = 0 ; j < hdr.group_num ; j++) {
        fal_partition_read(param_part_dev, offset, (uint8_t *)&group_info, sizeof(struct param_group_info));
        offset += sizeof(struct param_group_info);

        for(uint16_t i= 0 ; i < group_info.element_num; i++){
            fal_partition_read(param_part_dev, offset, (uint8_t *)&element_info, sizeof(struct param_element_info));
            offset += sizeof(struct param_element_info);

            _parse_eeprom(&group_info, &element_info);
        }

        fal_partition_read(param_part_dev, offset, spacer, sizeof(spacer));
        offset += sizeof(spacer);

        if (spacer[0] == 0x55 && spacer[1] == 0xAA) {
            result = 1;
        } else if (spacer[0] == 0x1F && spacer[1] == 0x04) {
            result = 2;
            break;
        } else {
            break;
        }
    }

    if (result == 0 || result == 1) {
        rt_kprintf("Incomplete parameter loading!\n");
    }

    param_total_size = offset;

    rt_kprintf("parameter total size: %u.\n", offset);
}

#else
void param_save_to_file(void)
{
    int fd, size,slen;
    if(fs_init_complete()){
        fd = open(PARAM_FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC);
        if(fd >= 0){

            /* add title */
            fs_fprintf(fd, "kind: parameter\n");

            param_t* p;
            param_group_t* gp;

            uint8_t group_num = PARAM_GROUP_COUNT;
            uint8_t group_num_half = group_num/2;

            for(int j = 0 ; j < group_num ; j++){
                gp = param_group_list[j];

                /* add group element */
                fs_fprintf(fd, "\n%s: \n", gp->name);
                
                p = gp->content;
                for(int i= 0 ; i < gp->param_num ; i++){
                    /* add param element */
                    fs_fprintf(fd, "\x20\x20\x20\x20%s: ", p->name);
                    
                    /* add value element */
                    if (p->type == PARAM_TYPE_INT8) {
                        fs_fprintf(fd, "%d\n", p->val.i8);
                    }

                    if (p->type == PARAM_TYPE_UINT8) {
                        fs_fprintf(fd, "%d\n", p->val.u8);
                    }

                    if (p->type == PARAM_TYPE_INT16) {
                        fs_fprintf(fd, "%d\n", p->val.i16);
                    }

                    if (p->type == PARAM_TYPE_UINT16) {
                        fs_fprintf(fd, "%d\n", p->val.u16);
                    }

                    if (p->type == PARAM_TYPE_INT32) {
                        fs_fprintf(fd, "%d\n", p->val.i32);
                    }

                    if (p->type == PARAM_TYPE_UINT32) {
                        fs_fprintf(fd, "%d\n", p->val.u32);
                    }

                    if (p->type == PARAM_TYPE_FLOAT) {
                        fs_fprintf(fd, "%f\n", p->val.f);
                    }

                    if (p->type == PARAM_TYPE_DOUBLE) {
                        fs_fprintf(fd, "%lf\n", p->val.lf);
                    }
                    
                    p++;
                }

                if (j == group_num_half) {
                    fsync(fd);
                }
            }
            fsync(fd);
            close(fd);
        }
    }
}

void param_load(void)
{
    int done = 0;
    int error = 0;

    yaml_parser_t parser;
    yaml_token_t token;

    PARAM_YAML_PARSE_STATE status = PARAM_YAML_PARSE_START;

    int fd = open(PARAM_FILE_NAME, O_RDONLY);

    if (fd < 0) {
        param_save_to_file();
        return;
    }

    assert(yaml_parser_initialize(&parser));
    yaml_parser_set_input(&parser, _yaml_read_handler, (void *)&fd);

    while (!done)
    {
        if (!yaml_parser_scan(&parser, &token)) {
            error = 1;
            rt_kprintf("yaml parse err\n");
            break;
        }

        _parse_yaml(&token, &status);

        done = (token.type == YAML_STREAM_END_TOKEN);

        yaml_token_delete(&token);
    }

    yaml_parser_delete(&parser);
    close(fd);

    rt_kprintf("parameter load success!\n");
}
#endif

void param_init(void)
{
    DIR *dirp;
    int ret;

    param_scan_list();
    param_interface_init();

#if defined(HAL_USE_EEPROM_PARAM)
    if ((param_part_dev = fal_partition_find(PARAM_FAL_PARTITION_NAME)) == NULL)
    {
        rt_kprintf("Device %s NOT found. Probe failed.\n", PARAM_FAL_PARTITION_NAME);
    }
#else
    /* 打开 / dir_test 目录 */
    dirp = opendir(PARAM_DIR);
    if (dirp == RT_NULL)
    {
        /* 创建目录 */
        ret = mkdir(PARAM_DIR, 0x777);
        if (ret < 0)
        {
            /* 创建目录失败 */
            rt_kprintf("[param]: dir error!\n");
            return;
        }
        else
        {
            /* 创建目录成功 */
            rt_kprintf("mkdir ok!\n");
        }
    }
#endif

    param_load();
    param_save();
}

static int _yaml_read_handler(void *data, unsigned char *buffer, size_t size, size_t *size_read)
{
    int fd = *((int *)data);

    if (fd < 0) {
        return 0;
    }

    *size_read = read(fd, buffer, size);

    return 1;
}

/*------------------------------------test------------------------------------*/


